package twoD.hofem;

import java.util.Arrays;

/**
 * Creates a piecewise defined function. Example: For the function
 * 
 * <pre>
 *        | x      if x &lt; 1
 * f(x) = | 1      if 1 &lt;= x &lt; 2
 *        | 1-0.5x if 2 &lt;= x &lt; 3
 * </pre>
 * 
 * @author Team 2
 */

public class PiecewiseFunctionRToR implements FunctionRToR {

	private FunctionRToR[] pieces_;
	private double[] a;
	private int n;

	public PiecewiseFunctionRToR(FunctionRToR... pieces) {

		pieces_ = pieces;
		n = pieces_.length;
		a = new double[n + 1];

		a[0] = pieces_[0].getDomain().getLower();

		for (int i = 0; i < n; i++) {
			a[i + 1] = pieces_[i].getDomain().getUpper();
		}

		for (int i = 0; i < n - 1; i++) {
			if (pieces_[i].getDomain().getUpper() != pieces_[i + 1].getDomain()
					.getLower()) {
				throw new IllegalArgumentException(" Domains Incompatible ");
			}
		}

	}

	/**
	 * Finds the interval in which x falls for the domains defined in the
	 * piecewise function using Binary Search
	 * 
	 * @param x
	 * @return domain serial number
	 */
	public int findInterval(double x) {

		int k = Arrays.binarySearch(a, x);
		if (k == 0)
			return k;
		else if (k > 0)
			return k - 1;
		else
			return - k - 2;
		
		/*
		int k = 0;

		k = Arrays.binarySearch(a, x);
		return -k - 2;
		*/
		
	}

	/**
	 *Returns the derivative of the piecewise function for the particular
	 * interval x falls in
	 * 
	 * 
	 */
	public double derivativeAt(double x) {

		int i = findInterval(x);
		return pieces_[i].derivativeAt(x);
	}

	/**
	 * Returns the breakpoints of the piecewise function. for eg:
	 * 
	 * <pre>
	 *        | x      if x &lt; 1
	 * f(x) = | 1      if 1 &lt;= x &lt; 2
	 *        | 1-0.5x if 2 &lt;= x &lt; 3
	 * </pre>
	 * 
	 * It would return an array [1, 2]
	 * 
	 */
	public double[] getBreakpoints() {
		double bps[] = new double[n - 1];

		for (int i = 0; i < n - 1; i++) {
			bps[i] = pieces_[i].getDomain().getUpper();
		}
		return bps;
	}

	public Interval getDomain() {

		Interval interval = new Interval(pieces_[0].getDomain().getLower(),
				pieces_[n - 1].getDomain().getUpper());
		return interval;
	}

	public int getP() {
		int p = 0;
		for (int i = 0; i < n; i++) {
			if (p < pieces_[i].getP()) {
				p = pieces_[i].getP();
			}
		}
		return p;
	}

	/**
	 * Returns the value at the specified point after evaluating the function in
	 * the interval in which it falls.
	 */

	public double valueAt(double x) {
		int i = findInterval(x);
		return pieces_[i].valueAt(x);
	}

}
